# Copyright (c) 2017 Presto Labs Pte. Ltd.
# Author: jshin

import math


class TickQty(object):
  def ceil(self, qty):
    raise NotImplementedError()

  def floor(self, qty):
    raise NotImplementedError()

  def round(self, qty):
    raise NotImplementedError()

  def format(self, qty):
    raise NotImplementedError()


class FixedTickQty(TickQty):
  def __init__(self, *, qty_precision, qty_multiplier=None, min_qty=None, max_qty=None):
    assert qty_precision >= 0
    assert int(qty_precision) == qty_precision
    if qty_multiplier is not None:
      assert int(qty_multiplier) == qty_multiplier

    self.qty_precision = qty_precision
    self.qty_multiplier = qty_multiplier
    self.power_qty_precision = 10**(-self.qty_precision)
    self.factor_qty_precision = 10.0**self.qty_precision
    self.min_qty = min_qty
    self.max_qty = max_qty

  def constrain(self, qty):
    if self.min_qty is not None and self.max_qty is not None:
      if qty >= self.min_qty and qty <= self.max_qty:
        return qty
      elif qty > self.max_qty:
        return self.max_qty
      elif qty < self.min_qty:
        return self.min_qty
      else:
        return None
    else:
      return qty

  def ceil(self, qty):
    return self.constrain(math.ceil(qty * self.factor_qty_precision) / self.factor_qty_precision)

  def floor(self, qty):
    return self.constrain(math.floor(qty * self.factor_qty_precision) / self.factor_qty_precision)

  def round(self, qty):
    return self.constrain(round(qty * self.factor_qty_precision) / self.factor_qty_precision)

  def format(self, qty):
    if self.qty_multiplier is not None:
      assert self.round(qty) % self.qty_multiplier == 0, qty
    if self.qty_precision is not None:
      return ('%%.%df' % self.qty_precision) % qty
    else:
      return '%f' % qty
